package com.emeraldparser.util;

import com.emeraldparser.util.Graph.Edge;
import com.google.common.base.Function;
import com.google.common.base.Functions;

public class Graphs {

	public static <V, E> Graph<V, E> invert(final Graph<V, E> graph) {
		return new InvertedGraph<V, E>() {
			@Override
			public Graph<V, E> delegate() {
				return graph;
			}
		};
	}

	public static <V, E> Function<Edge<V, E>, V> edgeSourceFunction() {
		return new Function<Edge<V, E>, V>() {
			@Override
			public V apply(Edge<V, E> edge) {
				return edge.getSource();
			}
		};
	}

	public static <V, E> Function<Edge<V, E>, V> edgeDestinationFunction() {
		return new Function<Edge<V, E>, V>() {
			@Override
			public V apply(Edge<V, E> edge) {
				return edge.getDestination();
			}
		};
	}

	public static <V, E> boolean addDirectedEdgesFromSource(
			Graph<? super V, ? super E> graph,
			V source,
			Iterable<? extends V> destinations,
			E edgeLabel) {
		boolean modified = false;
		
		for (V destination : destinations) {
			modified |= graph.addDirectedEdge(source, destination, edgeLabel);
		}
		
		return modified;
	}

	public static <V, E> boolean addDirectedEdgesToDestination(
			Graph<? super V, ? super E> graph,
			Iterable<? extends V> sources,
			V destination,
			E edgeLabel) {
		boolean modified = false;
		
		for (V source : sources) {
			modified |= graph.addDirectedEdge(source, destination, edgeLabel);
		}
		
		return modified;
	}

	public static <V_OLD, E_OLD, V_NEW, E_NEW> Graph<V_NEW, E_NEW> transform(
			Graph<? extends V_OLD, ? extends E_OLD> oldGraph,
			Function<? super V_OLD, ? extends V_NEW> vertexFunction,
			Function<? super E_OLD, ? extends E_NEW> edgeLabelFunction
		) {
		Graph<V_NEW, E_NEW> newGraph = SetGraph.create();
		transform(oldGraph, newGraph, vertexFunction, edgeLabelFunction);
		return newGraph;
	}

	public static <V_OLD, E, V_NEW> void transformVertices(
			Graph<? extends V_OLD, ? extends E> oldGraph,
			Graph<? super V_NEW, ? super E> newGraph,
			Function<? super V_OLD, ? extends V_NEW> vertexFunction
		) {
		transform(oldGraph, newGraph, vertexFunction, Functions.<E>identity());
	}

	public static <V_OLD, E_OLD, V_NEW, E_NEW> void transform(
			Graph<? extends V_OLD, ? extends E_OLD> oldGraph,
			Graph<? super V_NEW, ? super E_NEW> newGraph,
			Function<? super V_OLD, ? extends V_NEW> vertexFunction,
			Function<? super E_OLD, ? extends E_NEW> edgeLabelFunction
		) {
		for (V_OLD oldVertex : oldGraph.vertices()) {
			V_NEW newVertex = vertexFunction.apply(oldVertex);
			newGraph.addVertex(newVertex);
		}
		
		for (Graph.Edge<? extends V_OLD, ? extends E_OLD> oldEdge : oldGraph.edges()) {
			V_NEW newSource = vertexFunction.apply(oldEdge.getSource());
			V_NEW newDestination = vertexFunction.apply(oldEdge.getDestination());
			E_NEW newLabel = edgeLabelFunction.apply(oldEdge.getLabel());
			
			if (oldEdge.isDirected()) {
				newGraph.addDirectedEdge(newSource, newDestination, newLabel);
			} else {
				newGraph.addUndirectedEdge(newSource, newDestination, newLabel);
			}
		}
	}

}
